BizTalk
Server natively promotes an asynchronous messaging pattern and readily
embraces an event driven architecture. What BizTalk adds to the
standalone WCF patterns we've seen above is the injection of a message
broker. This middle layer loosely couples the enterprise systems on
both ends of the service call while enabling a new set of messaging
capabilities not available in standard service implementations.
Consuming asynchronous services
Consuming asynchronous
services from within BizTalk Server is an especially straightforward
task. However, the huge caveat is: BizTalk Server 2009 cannot execute
WCF services whose isOneWay flag is set
to true. Wait, so doesn't that mean that BizTalk does NOT support
asynchronous services? For me, it's a matter of perspective. BizTalk
CAN still consume WCF services in an asynchronous manner from
orchestration processes. Let's see how.
First modify the existing IAdverseEventSync interface to include an operation that returns no data.
[OperationContract]
void UpdateAdverseEvent(AdverseEvent modifiedAE);
For my implementation of this service, I included a processing delay of thirty seconds, and write a message to the machine's Application Event Log
afterwards. To make sure our service host reflects this new operation,
rebuild the service library and service host container projects.
Next, add a new BizTalk Server
Project to the existing Visual Studio.NET solution. Create a new schema
that represents the canonical Adverse Event entity. Mine looks like
this:
At this point we need to
reference the existing WCF service so that we can acquire the metadata
necessary for BizTalk to consume it. Remember that we don't do Add Service Reference for BizTalk projects, but rather, Add Generated Items and choose Consume WCF Service.
After plugging in the WCF
service URL to the wizard, we end up with the schemas we are seeking.
We will have to transform the Adverse Event data from the canonical
format to the service-specific structure, so add a new BizTalk Map
which performs this task.
Finally, let's
design an orchestration that takes in the canonical message, transforms
it to the service format, calls the service, and writes a message to
the machine's Application Event Log. The orchestration that I built is arranged like this:
A key thing to note: I created my own one-way orchestration send port because the orchestration port type auto-generated by the BizTalk WCF Service Consuming Wizard
is a two-way port that handles both the request message and empty
response acknowledgement. In my case, I don't want to wait for that
response and prefer to simply continue my workflow process. In essence,
I am telling BizTalk Server to treat this service invocation
asynchronously.
At this point, we can build and deploy the BizTalk project. In the BizTalk Administration Console,
we create the receive port/location necessary to pick up the canonical
schema format. As we decided to abandon the auto-generated type for our
orchestration send port, the auto-generated bindings (which are also
request/response) are equally unusable. So, define a new static one-way
send port that utilizes the WCF-WsHttp
adapter. The values in this adapter configuration should match your
service. This means that the address URI should match, the SOAP Action
should be properly set, and the security settings are in sync with the
service's expectations.
After binding the
orchestration to the new ports, and starting all relevant components,
the solution can be tested. Remember that our synchronous service has a
thirty second delay inside. So if this orchestration behaves like a
typical service client (i.e., waiting to proceed until the service
completes) we would not expect to see the orchestration's final log
message prior to the log message of the service. However, that's not
what happens in our just-built scenario. Testing what we've built
above, we can clearly see that the orchestration completes 30+ seconds
before the service finishes its processing.
So what does it gives
us? Remember how BizTalk works. Our one-way orchestration port means
that the orchestration is publishing to the bus and moving on. It's now
up to the send port to successfully deliver the message. The send port
still sees the service as synchronous and won't complete its processing
(and thus delete the message from the MessageBox) until the service
returns its acknowledgement. The send port behaves in a "store and
forward" fashion where the message is persisted until transmitted
successfully.
What if we throw an
exception from our service? By default, the send port message will
become suspended, but the error does not flow back to the
orchestration. It completes without knowing that an exception occurred.
What if we WANT the orchestration to catch business exceptions? We
could turn Delivery Notification
on for the orchestration's send port which means that the orchestration
waits for confirmation that the physical send port successfully
delivered its message. However, this doesn't work as you might expect,
as the orchestration still progresses to its next step immediately
after the message leaves the orchestration. The safest way for the
orchestration to catch any service exception is to switch back to the
request/response pattern and apply Scope shapes which catch targeted or general exceptions.